window.jQuery = window.jQuery || {};
window.jQuery.fn = window.jQuery.fn || {};

jQuery.fn.rpc = function(url, dataType, onLoadCallback, version) {
        version = version || "1.0";
        dataType = dataType || "json";
        if(dataType != "json" && dataType != "xml") {
                new Error("IllegalArgument: Unsupported data type");
        }
        var _self = this;
        
        var serializeToXml = function(data) {
                switch (typeof data) {
                case 'boolean':
                        return '<boolean>'+ ((data) ? '1' : '0') +'</boolean>';
                case 'number':
                        var parsed = parseInt(data);
                        if(parsed == data) {
                                return '<int>'+ data +'</int>';
                        }
                        return '<double>'+ data +'</double>';
                case 'string':
                        return '<string>'+ data +'</string>';
                case 'object':
                        if(data instanceof Date) {
                                return '<dateTime.iso8601>'+ data.getFullYear() + data.getMonth() + data.getDate() +'T'+ data.getHours() +':'+ data.getMinutes() +':'+ data.getSeconds() +'</dateTime.iso8601>';

                        } else if(data instanceof Array) {

                                var ret = '<array><data>'+"\n";

                                for (var i=0; i < data.length; i++) {

                                        ret += '  <value>'+ serializeToXml(data[i]) +"</value>\n";

                                }

                                ret += '</data></array>';

                                return ret;

                        } else {

                                var ret = '<struct>'+"\n";

                                jQuery.each(data, function(key, value) {

                                        ret += "  <member><name>"+ key +"</name><value>";

                                        ret += serializeToXml(value) +"</value></member>\n";

                                });

                                ret += '</struct>';

                                return ret;

                        }

                }

        }

        var xmlRpc = function(method, params) {

                var ret = '<?xml version="'+version+'"?><methodCall><methodName>'+method+'</methodName><params>';

                for(var i=0; i<params.length; i++) {

                        ret += "<param><value>"+serializeToXml(params[i])+"</value></param>";

                }

                ret += "</params></methodCall>";

                return ret;

        }

        var parseXmlValue = function(node) {

                childs = jQuery(node).children();

                for(var i=0; i < childs.length; i++) {

                        switch(childs[i].tagName) {

                        case 'boolean':

                                return (jQuery(childs[i]).text() == 1);

                        case 'int':

                                return parseInt(jQuery(childs[i]).text());

                        case 'double':

                                return parseFloat(jQuery(childs[i]).text());

                        case "string":

                                return jQuery(childs[i]).text();

                        case "array":

                                var ret = [];

                                jQuery("> data > value", childs[i]).each(

                                        function() {

                                                ret.push(parseXmlValue(this));

                                        }

                                );

                                return ret;

                        case "struct":

                                var ret = {};

                                jQuery("> member", childs[i]).each(

                                        function() {

                                                ret[jQuery( "> name", this).text()] = parseXmlValue(jQuery("value", this));

                                        }

                                );

                                return ret;

                        case "dateTime.iso8601":

                                /* TODO: fill me :( */

                                return NULL;

                        }

                }

        }

        var parseXmlResponse = function(data) {

                var ret = {};

                ret.version = version;

                jQuery("methodResponse params param > value", data).each(

                        function(index) {

                                ret.result = parseXmlValue(this);

                        }

                );

                jQuery("methodResponse fault > value", data).each(

                        function(index) {

                                ret.error = parseXmlValue(this);

                        }

                );

                return ret;

        }

        var rpc_contents = {

                'xml':'text/xml'

                ,'json':'application/json'

        };

        var _rpc = function(method, callback) {

                var params = [];

                for (var i=2; i<arguments.length; i++) {

                        params.push(arguments[i]);

                }

                console.log(params);

                var data;

                if(dataType == 'json') {

                        data = {"version":version, "method":method, "params":params};

                } else {

                        data = xmlRpc(method, params);

                }

                console.log(data);

                jQuery.ajax({

                        "url": url,

                        "dataType": dataType,

                        "type": 'POST',

                        "data": data,

                        "success": function(inp) {

                                var json = inp;

                                if(dataType == "xml") {

                                        json = parseXmlResponse(inp);

                                }

                                console.log("json response:", json);

                                callback(json);

                        },

                        "processData": false,

                        "contentType": rpc_contents[dataType]

                });

        };

        _rpc("system.listMethods", 

                function(json) {

                        console.log(json);

                        /* get the functions */

                        if(!json.result) {

                                return;

                        }

                        var proc = null;

                        for(var i = 0; i<json.result.length; i++) {

                                proc = json.result[i];

                                var obj = _self;

                                var objStack = proc.split(/\./);

                                for(var j = 0; j < (objStack.length - 1); j++){

                                        obj[objStack[j]] = obj[objStack[j]] || {};

                                        obj = obj[objStack[j]];

                                }

                                /* add the new procedure */

                                obj[objStack[j]] = (

                                        function(method, obj) {

                                                var _outer = {"method":method,"rpc":_rpc};

                                                return function(callback) {

                                                        var params = [];

                                                        params.push(_outer.method);

                                                        params.push(callback);

                                                        for (var i=1; i<arguments.length; i++) {

                                                                params.push(arguments[i]);

                                                        }

                                                        _rpc.apply(_self, params);

                                                }

                                        }

                                )(proc, _rpc);

                        }

                        console.log('Load was performed.');

                        if(onLoadCallback) {

                                onLoadCallback(_self);

                        }

                }

        );

};

